/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tweetbotserver.robot.comms;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import lejos.pc.comm.NXTComm;
import lejos.pc.comm.NXTCommFactory;
import lejos.pc.comm.NXTInfo;
import tweetbotserver.robot.comms.encoder.BotMessageEncoderManager;

/**
 *
 * @author Mike
 */
public class RobotCommunicationsManager {

    private DataOutputStream dataOutputStream = null;
    private DataInputStream dataInputStream = null;
    private String nxtName = "";
    private boolean nxtConnected = false;
    private boolean nxtStreamsCreated = false;
    private boolean started = false;
    private List<RobotCommunicationsListener> communicationListeners = null;
    private BotMessageReceiver botMessageReceiver = null;
    private BotMessageSender botMessageSender = null;
    private Queue<BotMessage> botMessageQueue = null;

    public RobotCommunicationsManager(String nxt_name) {
        nxtName = nxt_name;
        communicationListeners = new ArrayList<RobotCommunicationsListener>();
        botMessageQueue = new LinkedBlockingQueue<BotMessage>();
        botMessageReceiver = new BotMessageReceiver();
        botMessageSender = new BotMessageSender();
        System.out.println("Created CommunicationsManager for NXT:" + nxt_name);
    }

    public void addCommunicationsListener(RobotCommunicationsListener listener) {
        communicationListeners.add(listener);
    }

    synchronized private void informListeners(BotMessage message) {
        for (RobotCommunicationsListener listener : communicationListeners) {
            listener.handleBotMessage(message);
        }
    }

    public boolean connect() throws Exception {

        System.out.println("Initialising comms");
        NXTComm nxtComm = NXTCommFactory.createNXTComm(NXTCommFactory.BLUETOOTH);
        System.out.println("Opened comms OK");
        NXTInfo[] nxtInfo = nxtComm.search(nxtName, NXTCommFactory.BLUETOOTH);

        if (nxtInfo.length > 0) {
            System.out.println("Found NXT - Opening connection...");
            nxtConnected = nxtComm.open(nxtInfo[0]);

            if (nxtConnected) {
                System.out.println("Creating Streams...");
                OutputStream os = nxtComm.getOutputStream();
                dataOutputStream = new DataOutputStream(os);

                InputStream is = nxtComm.getInputStream();
                dataInputStream = new DataInputStream(is);
                nxtStreamsCreated = true;
                System.out.println("Created Streams.");

            } else {
                System.out.println("Not connected.");
            }

        } else {
            System.out.println("Could not find NXT");
        }

        return nxtConnected;
    }

    public boolean sendMessage(BotMessage message) throws Exception {
        boolean success = false;
        botMessageQueue.add(message);
        System.out.println("added bot message to queue:" + message);
        return success;
    }

    public void start() throws Exception {
        if (nxtConnected && nxtStreamsCreated) {
            // start a thread containing code which waits for messages to be received from bot

//          
            // upon receving a message then decode using

            if (!started) {

                Thread botMessageReceiverThread = new Thread(botMessageReceiver);
                Thread botMessageSenderThread = new Thread(botMessageSender);
                started = true;
                botMessageReceiverThread.start();
                botMessageSenderThread.start();

            } else {
                System.out.println("Already started.");
            }


        } else {
            throw new Exception("Cannot start CommunicationsManager: nxt not connected and/or streams not created.");
        }

    }

    public void stop() {
        if (started) {
            started = false;
        } else {
            System.out.println("Not started");
        }
    }

    class BotMessageReceiver implements Runnable {

        BotMessageReceiver() {
        }

        public void run() {
            System.out.println("BotMessageReceiver Thread starting.");
            try {
                while (started) {
                    Thread.sleep(500);

                   
                    System.out.println("Waiting for data from robot!");

                        byte[] data = new byte[50];
                        int size = dataInputStream.read(data);

                        System.out.println("Got data - size:" + size);

                        BotMessage receivedBotMessage = BotMessageEncoderManager.decode(data, String.class);

                        informListeners(receivedBotMessage);
                    
                  
                }
            } catch (InterruptedException exc) {
                System.out.println("BotMessageReceiver interrupted.");
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("BotMessageReceiver terminating.");
        }
    }

    class BotMessageSender implements Runnable {

        BotMessageSender() {
        }

        public void run() {
            System.out.println("BotMessageSender Thread starting.");
            try {
                while (started) {
                    Thread.sleep(5000);

                    if (!botMessageQueue.isEmpty()) {
                        System.out.print("processing queue...");
                        BotMessage message = botMessageQueue.remove();

                        if (nxtConnected && nxtStreamsCreated) {
                            try {
                                System.out.println("Attempting to send message:" + message);
                                byte[] dataToSend = BotMessageEncoderManager.encode(message);

                                if (dataToSend != null) {
                                    dataOutputStream.write(dataToSend);
                                    dataOutputStream.flush();
                                    System.out.println("Successfully sent message!");
                                } else {
                                    throw new Exception("Could not encode message!");
                                }
                            } catch (Exception e) {
                                System.out.println("Could not encode BotMessage to be sent:" + e.toString());
                            }
                        } else {
                            throw new Exception("NXT not connected and/or streams not created - Cannot send message!");
                        }

                    }
                }
            } catch (InterruptedException exc) {
                System.out.println("BotMessageSender interrupted.");
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("BotMessageSender terminating.");
        }
    }
}
